package com.zhoug.common.http.interceptors;

import android.util.Base64;

import com.zhoug.common.http.core.ContentType;
import com.zhoug.common.utils.LogUtils;
import com.zhoug.common.utils.MD5Utils;
import com.zhoug.common.utils.StringUtils;
import com.zhoug.common.utils.encrypt.AESHelper;
import com.zhoug.common.utils.encrypt.Base64Utils;
import com.zhoug.common.utils.encrypt.IEncryptor;
import com.zhoug.common.utils.encrypt.RSAHelper;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;

/**
 * 请求加密拦截器
 * 生成随机密匙使用AES/CBC加密数据,再用RSA加密生成的随机密匙放入header中传递给服务器
 *
 * @Author 35574
 * @Date 2021/10/29
 * @Description
 */
public class EncryptInterceptor implements Interceptor {
    protected static final String TAG = ">>>EncryptInterceptor";
    protected static final boolean debug = false;
    //文件
    protected static final String FileType = "application/otcet-stream";
    protected static final String OCTET_STREAM = "application/octet-stream";
    //json
    protected static final String JsonType = "application/json";
    //文本
    protected static final String TEXT = "text/plain";
    //form-urlencoded
    protected static final String FORM_URL_ENCODED = "application/x-www-form-urlencoded";


    protected final String CHARSET = "UTF-8";
    //编码方式
    protected Charset charset = StandardCharsets.UTF_8;
    protected int base64Flags = Base64.NO_WRAP;
    protected boolean openEncrypt = true;//是否加密
    //使用RAS加密AES的密匙的公钥
    private String rsaPublicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCsz9ehtLYrWS7s6sN+WGVBGuvcIc7d3/FF66YPO7LSoMItc8VYRlqsPbeEEp1C3xBzWjdg/GmILpjSiFVtfcNE+tiwJ1ktABQo8LZteKsMwTnPfk/7afVJKkJd+6u9tIcaXCJQ+65O8RIE57AWlT2yJdPLqDfa9e6+WpkLAbWh8QIDAQAB";
    //使用RAS解密服务器返回的AES的密匙的私钥
    private String rsaPrivateKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAP3bGZcV9L4ueenC5JbU6LolqqaAoWIRHjw7aTthAOCdplmp4YCMR8D7zp+AzAPIK/J5JQmLr6MqeApAxeCl5loEtWvm4jk/MQjj1aiQAVgLQDYEhfAld+VD4Z3CR7UZJOKjCZdvymrzEHE+EJRBEAPqFSyw4OG5Fp+U7Ku3FgPhAgMBAAECgYAw2DGKUp0u4r5ubiORqhHFj23Qb7udpPGOYVgUJswGbA8P/ZH4LAW6ajkNk7itmZxrIUgxGAGJgahrQJ9SZFl34onUxnXrJGAEz+ZriIBDmjFuAM3P7zC+IVvSoEe8jv/BjU/oDIdRQxWHg0+r2DB+YhYdfOLfkONnAD+7IPpZYQJBAP98zPmFvv8lIRL1Za0Oh4frUh6W6lAljzSDuXUFGwjq1N8oO8ceUEOAs5vDDtXGMz+aChkAzxs3DnLRIQZlxu0CQQD+XXYdlUntULP3yftd0P2IOTxd4Lc0w8s8wvPE8xWvBHzVG1oGo8jxxLcUdBxh+8PdQ81jcyrHtww1A3ocIj5FAkBaaDhEZ+6hbOsaQVo8dfLBxyTlDDRYLiJRy/cZkLfzXN39JciZeoGekXF9QfYC8cL607CVDnM7Ry1GtfhAwk01AkEA1JtV8KAprHw6ELD4s4/NeiEs/eKn/yhwJZdlnYb45tK/bHhyJP6dJMehpyzt1FBVhAn6EFfguBGd2GnRlNZiaQJAdRHIanZpHgl7yAsrKPuqM8BHESJUwm4V+rOs3fLUza0dFSZ/wLnGjr7SuYpgtvLCvGx4GeM7QO/K4rfR9DUUfw==";


    public EncryptInterceptor() {

    }


    @Override
    public Response intercept(Chain chain) throws IOException {
        if (openEncrypt) {
            //不需要加密
            if (!needEncryption(chain.request().url().toString())) {
                return chain.proceed(chain.request());
            }
            //加密请求
            Request request = encryptRequest(chain.request());
            //解密服务器返回
            return decryptResponse(chain.proceed(request));
        }
        return chain.proceed(chain.request());
    }

    /**
     * 加密请求
     *
     * @param request
     * @return
     * @throws IOException
     */
    private Request encryptRequest(Request request) throws IOException {
        if (debug) {
            LogUtils.d(TAG, "############加密request############");
        }
        //生成随机秘钥
        String aesKey = generatorAesKey();
        //创建加密器
        IEncryptor encryptor = createEncryptor(aesKey);
        //url参数加密
        URL url = request.url().url();
        String urlEncrypted = encryptUrlParams(url, encryptor);
        String method = request.method();
        if ("GET".equalsIgnoreCase(method)) {
            return request.newBuilder()
                    .url(urlEncrypted)
                    //添加公匙到header
                    .removeHeader(getKeyNameInHeader())//防止添加多个同名
                    .addHeader(getKeyNameInHeader(), encryptKey(aesKey))//把加密后的AES密匙放入header
                    .build();
        } else if ("POST".equalsIgnoreCase(method)) {
            //body加密
            RequestBody body = request.body();
            if (body != null) {
                if (debug) {
                    LogUtils.d(TAG, "intercept:body:" + body.toString());
                }
                //表单
                if (body instanceof MultipartBody) {
                    MultipartBody multipartBody = encryptMultipartBody((MultipartBody) body, encryptor);
                    Request.Builder builder1 = new Request.Builder();
                    return builder1.url(urlEncrypted)
                            .tag(request.tag())
                            .headers(request.headers())
                            .method(request.method(), multipartBody)
                            //添加公匙到header
                            .removeHeader(getKeyNameInHeader())//防止添加多个同名
                            .addHeader(getKeyNameInHeader(), encryptKey(aesKey))//把加密后的AES密匙放入header
                            .build();
                } else {
                    //非表单
                    Buffer buffer = new Buffer();
                    body.writeTo(buffer);
                    String s = buffer.readUtf8();
                    String encrypt = encryptor.encryptToBase64(s);
                    if (debug) {
                        LogUtils.d(TAG, "加密前:" + s);
                        LogUtils.d(TAG, "加密后:" + encrypt);
                    }
                    RequestBody requestBody = RequestBody.create(MediaType.parse(ContentType.TEXT_PLAIN), encrypt);
                    Request.Builder newBuilder = request.newBuilder();
                    newBuilder.method(request.method(), requestBody)
                            //添加公匙到header
                            .removeHeader(getKeyNameInHeader())//防止添加多个同名
                            .addHeader(getKeyNameInHeader(), encryptKey(aesKey));//把加密后的AES密匙放入header
                    return newBuilder.build();
                }
            }
        }
        return request;
    }

    /**
     * 加密url中的参数
     *
     * @param url
     * @param encryptor 加密器
     * @return
     * @throws UnsupportedEncodingException
     */
    protected String encryptUrlParams(URL url, IEncryptor encryptor) throws UnsupportedEncodingException {
        String urlString = url.toString();
        if (debug) {
            LogUtils.d(TAG, "intercept:加密前url:" + urlString);
        }
        String query = url.getQuery();
        if (!StringUtils.isNullOrEmpty(query)) {
            LogUtils.d(TAG, "intercept:请求参数加密前:" + query);
            String encrypt = encryptor.encryptToBase64(query);
            String info = "info=" + URLEncoder.encode(encrypt, CHARSET);
            LogUtils.d(TAG, "intercept:请求参数加密后:" + encrypt);
            LogUtils.d(TAG, "intercept:请求参数加密后URLEncoder:" + info);
            urlString = urlString.replace(url.getQuery(), info);
        }
        if (debug) {
            LogUtils.d(TAG, "intercept:加密后url:" + urlString);
        }
        return urlString;
    }

    /**
     * 加密表单
     */
    protected MultipartBody encryptMultipartBody(MultipartBody multipartBody, IEncryptor encryptor) throws IOException {
        if (debug) {
            LogUtils.d(TAG, "intercept:is MultipartBody");
        }
        List<MultipartBody.Part> parts = multipartBody.parts();
        List<MultipartBody.Part> encryptParts = new ArrayList<>();//加密后的
        if (parts != null && parts.size() > 0) {
            for (int i = 0; i < parts.size(); i++) {
                MultipartBody.Part part = parts.get(i);
                RequestBody partBody = part.body();
                if (partBody != null) {
                    MediaType type = partBody.contentType();
                    if (type != null && FileType.equalsIgnoreCase(type.toString())) {
                        //加密文件
                        encryptParts.add(encryptFile(part));
                    } else {
                        Buffer buffer = new Buffer();
                        partBody.writeTo(buffer);
                        String s = buffer.readUtf8();
                        String encrypt = encryptor.encryptToBase64(s);
                        if (debug) {
                            LogUtils.d(TAG, "intercept:加密前=" + s);
                            LogUtils.d(TAG, "intercept:加密后=" + encrypt);
                        }
                        try {
                            RequestBody requestBody = RequestBody.create(type, encrypt);
                            MultipartBody.Part newPart = MultipartBody.Part.create(part.headers(), requestBody);
                            encryptParts.add(newPart);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }

            }
        }
        //使用加密后的数据创建新的body
        if (encryptParts.size() > 0) {
            MultipartBody.Builder builder = new MultipartBody.Builder();
            for (MultipartBody.Part p : encryptParts) {
                builder.addPart(p);
            }
            builder.setType(multipartBody.type());
            return builder.build();
        }
        return multipartBody;
    }

    /**
     * 加密文件
     *
     * @param part
     * @return
     */
    protected MultipartBody.Part encryptFile(MultipartBody.Part part) {
        return part;//默认文件不加密
    }


    /**
     * 解密返回
     *
     * @param response
     * @return
     * @throws IOException
     */
    protected Response decryptResponse(Response response) throws IOException {
        if (debug) {
            LogUtils.d(TAG, "############解密response############");
        }
        if (response.isSuccessful()) {
            ResponseBody body = response.body();
            if (body != null) {
                MediaType contentType = body.contentType();
                if (contentType != null) {
                    String mediaType = contentType.toString();
                    if (debug) {
                        LogUtils.d(TAG, "mediaType:" + mediaType);
                    }
                    if (needDecrypt(mediaType)) {
                        if (debug) {
                            LogUtils.d(TAG, "数据解密");
                        }
                        //base64编码吗后的数据
                        byte[] bytes = body.bytes();
                        String serialNumber = response.header(getKeyNameInHeader());
                        if (debug) {
                            LogUtils.d(TAG, "解密前serialNumber:" + serialNumber);
                        }
                        serialNumber = decryptKey(serialNumber);
                        if (debug) {
                            LogUtils.d(TAG, "解密后serialNumber:" + serialNumber);
                        }
                        AESHelper aesHelper = createEncryptor(serialNumber);
                        Base64Utils base64Utils = aesHelper.getBase64Utils();
                        String decrypt = aesHelper.decryptToString(base64Utils.decode(bytes));
                        if (debug) {
                            LogUtils.d(TAG, "解密前:" + base64Utils.getString(bytes));
                            LogUtils.d(TAG, "解密后:" + decrypt);
                        }
                        return response.newBuilder()
                                .body(ResponseBody.create(contentType, decrypt))
                                .build();
                    }

                }

            }
        }
        return response;
    }



    /**
     * 生成随机秘钥,用于加密数据
     *
     * @return
     */
    protected String generatorAesKey() {
        String replace = UUID.randomUUID().toString().replace("-", "");
        return MD5Utils.getMD5(replace);
    }


    /**
     * 创建加密器
     *
     * @param key 秘钥
     * @return
     */
    protected AESHelper createEncryptor(String key) {
        return new AESHelper.Builder()
                .setCharset(charset)
                .setBase64Flag(base64Flags)
                .setKey(key)
                .setIv("S475445DF4GD5FG4")
                .setTransformation("AES/CBC/PKCS7Padding")
                .build();

    }

    /**
     * 加密密匙
     *
     * @param key
     * @return
     */
    protected String encryptKey(String key) {
        IEncryptor iEncryptor = new RSAHelper.Builder()
                .setCharset(charset)
                .setBase64Flag(base64Flags)
                .setPublicKey(rsaPublicKey)
                .build();
        return iEncryptor.encryptToBase64(key);
    }

    /**
     * 使用RAS解密AES的密匙
     *
     * @param key 被RAS加密后的AES密匙base64字符串
     * @return AES的密匙
     */
    public String decryptKey(String key) {
        IEncryptor iEncryptor = new RSAHelper.Builder()
                .setCharset(charset)
                .setBase64Flag(base64Flags)
                .setPrivateKey(rsaPrivateKey)
                .build();
        return iEncryptor.decryptToString(key);
    }

    /**
     * 放入header中的加密后的密匙的key,默认值:SerialNumber
     *
     * @return
     */
    protected String getKeyNameInHeader() {
        return "SerialNumber";
    }


    /**
     * 是否需要加密
     *
     * @return
     */
    protected boolean needEncryption(String url) {
        //需要加密的接口的 base url
        return url != null && url.startsWith("http://");
    }

    /**
     * 更具服务器返回的contentType判断是否需要解密
     *
     * @param contentType
     * @return
     */
    private boolean needDecrypt(String contentType) {
        if (contentType == null) {
            return false;
        }
        return contentType.contains(ContentType.JSON)
                || contentType.contains(ContentType.TEXT_PLAIN);
    }

}
